
package com.netty.session;

import com.netty.constant.Constants;
import com.netty.helper.ChannelGroupHelper;
import com.netty.model.Message;
import com.netty.model.MessageWrapper;
import com.netty.model.SocketSession;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.directwebremoting.ScriptSession;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * session管理
 *
 * @author suzhen
 */
@Slf4j
@Service
public class SocketSessionManagerImpl implements SocketSessionManager {

    /**
     * 保存所有session
     */
    private Map<String, SocketSession> sessions = new ConcurrentHashMap<>();

    @Override
    public void addSession(SocketSession session) {
        sessions.put(session.getAccount(), session);
        if (session.getSource() != Constants.ImserverConfig.DWR) {
            ChannelGroupHelper.add(session.getSession());
        }
        //添加上线通知
        log.debug("put a session " + session.getAccount() + " to sessions!");
        log.debug("session size " + sessions.size());
    }

    @Override
    public void updateSession(SocketSession session) {
        session.setUpdateTime(System.currentTimeMillis());
        sessions.put(session.getAccount(), session);
    }

    @Override
    public void removeSession(String sessionId) {
        try {
            SocketSession session = getSession(sessionId);
            if (session != null) {
                session.closeAll();
                sessions.remove(sessionId);

                //ChannelGroupHelper.remove(session.getSession());
                //添加下线通知
            }
        } catch (Exception ignored) {

        }
        log.debug("session size " + sessions.size());
        log.info("system remove the session " + sessionId + " from sessions!");
    }

    @Override
    public void removeSession(String sessionId, String nid) {
        SocketSession session = getSession(sessionId);
        if (session != null) {
            int source = session.getSource();
            if (source == Constants.ImserverConfig.WEBSOCKET || source == Constants.ImserverConfig.DWR) {
                session.close(nid);
                //判断没有其它session后 从SessionManager里面移除
                if (session.otherSessionSize() < 1) {
                    sessions.remove(sessionId);
                    //添加下线通知

                }
            } else {
                session.close();
                sessions.remove(sessionId);
                //添加下线通知
            }
        }

        log.info("remove the session " + sessionId + " from sessions!");
    }

    @Override
    public SocketSession getSession(String sessionId) {
        return sessions.get(sessionId);
    }

    @Override
    public SocketSession[] getSessions() {
        return sessions.values().toArray(new SocketSession[0]);
    }

    @Override
    public Set<String> getSessionKeys() {
        return sessions.keySet();
    }

    @Override
    public int getSessionCount() {
        return sessions.size();
    }

    @Override
    public SocketSession createSession(MessageWrapper wrapper, ChannelHandlerContext ctx) {
        String sessionId = wrapper.getSessionId();
        log.debug("createSession sessionId: {}", sessionId);
        SocketSession session = sessions.get(sessionId);
        //更新session
        if (session != null) {
            log.info("session " + sessionId + " exist!");
            //当链接来源不是同一来源或者 是socket链接，踢掉已经登录的session
            if (session.getSource() == Constants.ImserverConfig.SOCKET) {
                // 如果session已经存在则销毁session
                //从广播组清除
                log.info("sessionId" + session.getSessionId() + "---" + ctx.channel().id().asShortText() + "      !");
                ChannelGroupHelper.remove(session.getSession());
                session.close(session.getSessionId());
                sessions.remove(session.getAccount());
                log.info("session " + sessionId + " have been closed!");
            } else if (session.getSource() == Constants.ImserverConfig.WEBSOCKET) {
                //用于解决websocket多开页面session被踢下线的问题
                SocketSession newSession = setSessionContent(ctx, wrapper, sessionId);
                session.addSessions(newSession);
                updateSession(session);
                ChannelGroupHelper.add(newSession.getSession());
                log.info("session " + sessionId + " update!");
                return newSession;
            } else if (session.getSource() == Constants.ImserverConfig.DWR) {
                //清除dwr session
                log.info("sessionId ----" + session.getAccount() + " start remove !");
                session.closeAll();
                sessions.remove(session.getAccount());
                log.info("session " + sessionId + " have been closed!");
            }
        }

        session = setSessionContent(ctx, wrapper, sessionId);
        addSession(session);
        return session;
    }

    @Override
    public SocketSession createSession(ScriptSession scriptSession, String sessionId) {
        SocketSession dwrSession = new SocketSession(scriptSession);
        dwrSession.setAccount(sessionId);
        dwrSession.setSource(Constants.ImserverConfig.DWR);
        dwrSession.setPlatform((String) scriptSession.getAttribute(Constants.DWRConfig.BROWSER));
        dwrSession.setPlatformVersion((String) scriptSession.getAttribute(Constants.DWRConfig.BROWSER_VERSION));
        dwrSession.setBindTime(System.currentTimeMillis());
        dwrSession.setUpdateTime(System.currentTimeMillis());
        SocketSession session = sessions.get(sessionId);
        if (session != null) {
            log.info("session " + sessionId + " exist!");
            if (session.getSource() != Constants.ImserverConfig.DWR) {
                //从广播组清除
                log.info("sessionId ----" + session.getAccount() + " start remove !");
                ChannelGroupHelper.remove(session.getSession());
                List<Channel> channels = session.getSessionAll();
                if (channels != null && channels.size() > 0) {
                    for (Channel cl : channels) {
                        ChannelGroupHelper.remove(cl);
                    }
                }
                //session.close();
                sessions.remove(session.getAccount());
                log.info("session " + sessionId + " have been closed!");
            } else if (session.getSource() == Constants.ImserverConfig.DWR) {
                session.addSessions(dwrSession);
                updateSession(session);
                log.info("session " + sessionId + " update!");
                return dwrSession;
            }
        }
        addSession(dwrSession);
        return dwrSession;
    }

    @Override
    public boolean exist(String sessionId) {
        return getSession(sessionId) != null;
    }

    /**
     * 设置session内容
     *
     * @param ctx
     * @param wrapper
     * @param sessionId
     * @return
     */
    private SocketSession setSessionContent(ChannelHandlerContext ctx, MessageWrapper wrapper, String sessionId) {
        log.info("create new session " + sessionId + ", ctx -> " + ctx.toString());
        SocketSession session = new SocketSession(ctx.channel());
        Message model = (Message) wrapper.getBody();
        session.setAccount(sessionId);
        session.setSource(wrapper.getSource());
        session.setAppKey(model.getAppKey());
        session.setDeviceId(model.getDeviceId());
        session.setPlatform(model.getPlatForm());
        session.setPlatformVersion(model.getPlatFormVersion());
        session.setSign(model.getSign());
        session.setBindTime(System.currentTimeMillis());
        session.setUpdateTime(session.getBindTime());
        log.info("create new session " + sessionId + " successful!");
        return session;
    }
}
